#pragma once
#include "data_source.h"
#include <stdio.h>
#include <exception>

DataSource* _instance = NULL;

void checkInstance()
{
	if (_instance == NULL)
		throw new std::exception("instancia nula");
}
DataSource::DataSource( Ihandle* parent )
{
	_isStopped = false;
	_arDataChanged = true;
	_azimuth = _pitch = _roll = 0;
	createUI(parent);
}

int sliderChanged(Ihandle *ih)
{
	checkInstance();
	_instance->updateSliders();
	 return IUP_DEFAULT;
}

int arData_clicked(Ihandle *ih)
{
	checkInstance();
	_instance->selectNewARData();
	return IUP_DEFAULT;
}

int controlsChanged(Ihandle *ih)
{
	checkInstance();
	_instance->update();
	return IUP_DEFAULT;
}

int exit_cb( void )
{
	return IUP_CLOSE;
}


Ihandle* DataSource::initSliders( void )
{
	Ihandle* sliderBox, *azimuthLabel, * pitchLabel, *rollLabel;

	_azimuthVal = IupVal( "HORIZONTAL" );
	_pitchVal   = IupVal( "HORIZONTAL" );
	_rollVal    = IupVal( "HORIZONTAL" );

	azimuthLabel = IupLabel( "Azimuth: " );
	pitchLabel   = IupLabel( "Pitch: " );
	rollLabel    = IupLabel( "roll: " );

	_azimuthTxt = IupText( 0  );
	_pitchTxt   = IupText( 0 );
	_rollTxt    = IupText( 0 );

	IupSetCallback( _azimuthTxt, "VALUECHANGED_CB", ( Icallback )controlsChanged );
	IupSetCallback( _pitchTxt  , "VALUECHANGED_CB", ( Icallback )controlsChanged );
	IupSetCallback( _rollTxt   , "VALUECHANGED_CB", ( Icallback )controlsChanged );
	
	IupSetAttribute( azimuthLabel, "SIZE", "40x" );
	IupSetAttribute( pitchLabel  , "SIZE", "40x" );
	IupSetAttribute( rollLabel   , "SIZE", "40x" );

	IupSetAttribute( _azimuthVal, "MIN", "-180" );
	IupSetAttribute( _pitchVal  , "MIN", "-90" );
	IupSetAttribute( _rollVal   , "MIN", "-180" );

	IupSetAttribute( _azimuthVal, "MAX", "180" );
	IupSetAttribute( _pitchVal  , "MAX", "90" );
	IupSetAttribute( _rollVal   , "MAX", "180" );

	IupSetAttribute( _azimuthVal, "SIZE", "280x15" );
	IupSetAttribute( _pitchVal  , "SIZE", "280x15" );
	IupSetAttribute( _rollVal   , "SIZE", "280x15" );

	IupSetAttribute( _azimuthVal, "VALUE", "0" );
	IupSetAttribute( _pitchVal  , "VALUE", "-90" );
	IupSetAttribute( _rollVal   , "VALUE", "0" );

	IupSetCallback( _azimuthVal, "VALUECHANGED_CB", ( Icallback )sliderChanged );
	IupSetCallback( _pitchVal  , "VALUECHANGED_CB", ( Icallback )sliderChanged );
	IupSetCallback( _rollVal   , "VALUECHANGED_CB", ( Icallback )sliderChanged );
	updateSliders();
	

	sliderBox = IupVbox( IupHbox( azimuthLabel,_azimuthTxt, _azimuthVal, NULL ), IupHbox( pitchLabel,_pitchTxt, _pitchVal, NULL ), IupHbox( rollLabel,_rollTxt, _rollVal, NULL ), NULL );

	return sliderBox;
}

Ihandle* DataSource::initImageOpener( void )
{
	Ihandle* arDataLb = IupLabel( "AR Data:" );
	_arDataTxt = IupText( 0 );
	Ihandle* arDataBt = IupButton("selecionar", 0 );
	IupSetAttribute( _arDataTxt, IUP_EXPAND, IUP_YES );	
	IupSetAttribute( _arDataTxt, IUP_READONLY, IUP_YES );	
	IupSetCallback( arDataBt   , IUP_ACTION , ( Icallback )arData_clicked );

	return IupHbox( arDataLb, _arDataTxt, arDataBt, NULL );
}


void DataSource::createUI( Ihandle* parent )
{	
	/* create the dialog and set its attributes */
	_dialog = IupDialog( IupVbox( initImageOpener(), initSliders(), NULL ) );
	IupSetAttribute( _dialog, "TITLE", "Data Source" );	
	//IupSetAttribute( _dialog, "MENUBOX", IUP_NO );	
	IupSetAttribute( _dialog, IUP_RESIZE, IUP_NO );	
	IupSetAttributeHandle( _dialog, IUP_PARENTDIALOG, parent );
	IupSetAttribute( _dialog, "CLOSE_CB", "exit_cb" );
	
	IupShowXY( _dialog, IUP_RIGHT, IUP_CENTER );
}

DataSource* DataSource::init( Ihandle* parent, const char * device /*= NULL*/ )
{
	if (_instance == NULL)
	{
		DataSource* ds = new DataSource(parent);
		if(ds->initCamera(device))// so cria o datasource se conseguir pegar uma camera ou um video
			_instance = ds;	
		else
			delete ds;
	}
	return _instance;
}

bool DataSource::initCamera( const char* device )
{
	if( device == NULL || ( device != NULL && strlen( device ) == 1 && isdigit( device[0] ) ) )
		_capture.open( device != NULL ? device[0] - '0' : 0 );
	else 
		_capture.open( device );

	if( !_capture.isOpened() )
	{
		printf("Could not initialize capturing...\n");
		return false;
	}
	return true;
}

void DataSource::update()
{
	char* v = IupGetAttribute( _azimuthTxt , "VALUE" );
	IupSetAttribute( _azimuthVal, "VALUE", v );
	_azimuth = ( float )atof( v );

	v = IupGetAttribute( _pitchTxt , "VALUE" );
	IupSetAttribute( _pitchVal, "VALUE", v );
	_pitch = ( float )atof( v );

	v = IupGetAttribute( _rollTxt , "VALUE" );
	IupSetAttribute( _rollVal, "VALUE", v );
	_roll = ( float )atof( v );

}

void DataSource::updateSliders()
{
	IupSetAttribute( _azimuthTxt, "VALUE", IupGetAttribute( _azimuthVal , "VALUE" ) );
	IupSetAttribute( _pitchTxt, "VALUE", IupGetAttribute( _pitchVal , "VALUE" ) );
	IupSetAttribute( _rollTxt, "VALUE", IupGetAttribute( _rollVal , "VALUE" ) );	
	update();
}

void DataSource::getSensorFused( float* az, float* pitch, float* roll )
{
	*az= _azimuth;
	*pitch= _pitch;
	*roll= _roll;
}

void DataSource::getCameraFrame( cv::Mat& frame )
{
	if(!_isStopped)
		_capture >> _lastFrame;
	_lastFrame.copyTo(frame);	
}

DataSource::~DataSource()
{
	if (_capture.isOpened())
		_capture.release();	
}

void DataSource::selectNewARData()
{
	char imagePath[4096]="/*.png";

	int result = IupGetFile(imagePath);

	if( result  == 0 )
	{
		int numMarkerLoaded = 0;
		while(numMarkerLoaded < 10)
		{
			std::string imagePathStr = imagePath;
			if(imagePathStr.length() > 4 && isdigit(imagePathStr[imagePathStr.length()-5]))
			{
				imagePathStr[imagePathStr.length()-5] = '0'+numMarkerLoaded;
			}
			cv::Mat markerImage = cv::imread( imagePathStr );
			if( markerImage.data == NULL )
			{
				if(numMarkerLoaded == 0 )
					IupMessage("Problemas no carregamento","a imagem selecionada apresentou erro durante o carregamento");
				break;
			}

			std::string overPath = std::string( imagePathStr );
			overPath.insert( overPath.length() - 4, "_over" );
			cv::Mat overImage = cv::imread( overPath  );

			if( overImage.data == NULL )
			{
				IupMessage("Aviso: Problemas no carregamento da imagem para sobreposicao",(std::string("arquivo inexistente: ")+overPath).c_str());
				break;
			}

			std::string alphaPath = std::string( imagePathStr );
			alphaPath.insert( alphaPath.length() - 4, "_alpha" );
			cv::Mat alphaImage = cv::imread( alphaPath , 0 );

			if( alphaImage.data == NULL )
			{
				IupMessage("Aviso: Problemas no carregamento do alpha",(std::string("arquivo inexistente: ")+alphaPath).c_str());
				break;
			}
			if (numMarkerLoaded == 0)
			{
				_markers.clear();
				_imagesToOverlapAlpha.clear();
				_imagesToOverlap.clear();
			}
			_markers.push_back(markerImage);
			_imagesToOverlap.push_back(overImage);
			_imagesToOverlapAlpha.push_back(alphaImage);
			numMarkerLoaded++;
			if(imagePathStr.length() < 5 || !isdigit(imagePathStr[imagePathStr.length()-5]))
			{
				break;
			}
		}
		if(numMarkerLoaded>0)
		{
			IupSetAttribute(_arDataTxt,IUP_VALUE,imagePath);
			_arDataChanged = true;			
			IupMessage("Alguns marcadores carregados com sucesso",std::string("Sucesso: ").append(std::to_string((long long)numMarkerLoaded)).append(" marcadores foram carregados").c_str());
		}

	}else if( result  == 1 )
	{
		IupMessage("Problemas no carregamento","selecione a imagem de um marcador!");
	}
}

bool DataSource::arDataChanged()
{
	if(_arDataChanged)
	{
		_arDataChanged = false;
		return true;
	}
	return false;
}

void DataSource::getDataFrame( int markerPos,cv::Mat** dframe,cv::Mat** dframe_mask, int *marker_width,int *marker_height )
{
	if (markerPos > _imagesToOverlap.size() || markerPos > _imagesToOverlapAlpha.size())
	{
		throw new std::exception("posicao de marcador invalida");
	}

	*dframe = &(_imagesToOverlap[markerPos]);
	*dframe_mask = &(_imagesToOverlapAlpha[markerPos]);
	*marker_height = _markers[markerPos].rows;
	*marker_width = _markers[markerPos].cols;
}

const std::vector<cv::Mat>& DataSource::getMarkers()
{
	return _markers;
}


